package cn.mw.cmdb.service.impl;

import cn.mw.cmdb.entity.DataSourceInfo;
import cn.mw.cmdb.entity.DataSourceSyncInstanceInfo;
import cn.mw.cmdb.entity.DataSourceSyncResultInfo;
import cn.mw.cmdb.enums.DatasourceSyncTypeEnum;
import cn.mw.cmdb.mongoMapper.DatasourceInfoMapper;
import cn.mw.cmdb.mongoMapper.DatasourceSyncInstanceInfoMapper;
import cn.mw.cmdb.mongoMapper.DatasourceSyncResultInfoMapper;
import cn.mw.cmdb.param.DataSourceSyncResultQueryParam;
import cn.mw.cmdb.service.DataSourceService;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.cmdb.view.ViewConvertContext;
import cn.mw.cmdb.view.ViewManager;
import cn.mw.components.mongodb.entity.SearchParam;
import cn.mw.components.mongodb.tool.CriteriaWrapper;
import cn.mw.microMonitorCommon.api.cmdb.MwDatasourceSyncService;
import cn.mw.microMonitorCommon.api.common.MwTPServerService;
import cn.mw.microMonitorCommon.api.powerjob.PowerJobRequestParam;
import cn.mw.microMonitorCommon.api.powerjob.PowerJobService;
import cn.mw.microMonitorCommon.api.powerjob.PowerJobTimeExpressionParam;
import cn.mw.microMonitorCommon.entity.cmdb.DataSourceMappingRuleInfo;
import cn.mw.microMonitorCommon.entity.cmdb.DataSourcePropertyRelInfo;
import cn.mw.microMonitorCommon.entity.cmdb.DataSourceSyncResultVo;
import cn.mw.microMonitorCommon.entity.cmdb.TableHeaderItem;
import cn.mw.microMonitorCommon.entity.common.MwTPServerTable;
import cn.mw.microMonitorCommon.enums.ServerTypeEnum;
import cn.mw.microMonitorCommon.enums.TimeUnitEnum;
import cn.mw.microMonitorCommon.util.IDModelType;
import cn.mw.microMonitorCommon.util.ModuleIDManager;
import cn.mw.microMonitorCommon.util.SpringUtils;
import cn.mw.microMonitorCommon.utils.DatabaseUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@Slf4j
public class DatasourceServiceImpl extends BaseSearchService<DataSourceInfo> implements DataSourceService {

    @Autowired
    private DatasourceInfoMapper datasourceInfoMapper;

    @Autowired
    private DatasourceSyncResultInfoMapper datasourceSyncResultInfoMapper;

    @Autowired
    private DatasourceSyncInstanceInfoMapper datasourceSyncInstanceInfoMapper;

    @Autowired
    private ModuleIDManager moduleIDManager;

    @DubboReference(check = false ,timeout = 15000)
    private PowerJobService powerJobService;

    @DubboReference(check = false, timeout = 120000, mock = "true")
    private MwDatasourceSyncService mwDatasourceSyncService;

    @DubboReference(check = false, mock = "true" ,timeout = 15000)
    private MwTPServerService mwTPServerService;

    private static final String TASK_PROCESSOR = "mwDatasourceSyncProcessor";

    @Autowired
    private ViewManager viewManager;

    @Override
    protected DataSourceInfo genObject() {
        return new DataSourceInfo();
    }

    @Override
    public boolean addOrUpdateDataSource(DataSourceInfo dataSourceInfo) {
        String webMonitorModelId = SpringUtils.getPropertiesValue("base.webMonitor.modelId");
        // web监测模型不需要映射规则及过滤规则
        if (CollectionUtils.isNotEmpty(dataSourceInfo.getTargetModelIdPath())
                && StringUtils.equals(webMonitorModelId, dataSourceInfo.getTargetModelIdPath().get(dataSourceInfo.getTargetModelIdPath().size() - 1))) {
            dataSourceInfo.setMappingRule(new ArrayList<>());
            dataSourceInfo.setDataSourceFilterTypeList(new ArrayList<>());
            dataSourceInfo.setAutoScanFlag(false);
        }
        if (StringUtils.isBlank(dataSourceInfo.getId())) {
            // 执行新增
            dataSourceInfo.setId(moduleIDManager.getIDStr(IDModelType.Model));
            createAsyncJob(dataSourceInfo);
            datasourceInfoMapper.insert(dataSourceInfo);
        } else {
            Query query = null;
            try {
                query = new Query(CriteriaWrapper.where(DataSourceInfo::getId).is(dataSourceInfo.getId()));
                DataSourceInfo existsInfo = datasourceInfoMapper.findOne(query);
                if (existsInfo != null) {
                    if (existsInfo.getJobId() != null) {
                        powerJobService.deleteJob(List.of(existsInfo.getJobId()));
                    }
                    createAsyncJob(dataSourceInfo);
                    Update update = CMDBTool.genMongoUpdate(dataSourceInfo, null, null);
                    CMDBTool.updateFirst(datasourceInfoMapper, query, update);
                }
            } catch (Exception e) {
                throw new RuntimeException("update datasource error!", e);
            }
        }
        return true;
    }

    private void createAsyncJob(DataSourceInfo dataSourceInfo) {
        if (DatasourceSyncTypeEnum.auto.getType().equals(dataSourceInfo.getSyncType())) {
            // 自动同步方式需调powerjob接口创建定时任务
            Long jobId = powerJobService.saveOrUpdateJob(getJobParams(dataSourceInfo)).getData();
            if (jobId == null) {
                throw new RuntimeException("create async job error!");
            }
            dataSourceInfo.setJobId(jobId);
        }
    }

    private PowerJobRequestParam getJobParams(DataSourceInfo dataSourceInfo) {
        PowerJobRequestParam powerJobRequestParam = new PowerJobRequestParam();
        powerJobRequestParam.setJobName(String.format("%s_%s", dataSourceInfo.getId(), dataSourceInfo.getRuleName()));
        powerJobRequestParam.setJobParams(JSON.toJSONString(dataSourceInfo));
        PowerJobTimeExpressionParam powerJobTimeExpressionParam = new PowerJobTimeExpressionParam();
        powerJobTimeExpressionParam.setInterval(calcInterval(dataSourceInfo.getSyncTime(), dataSourceInfo.getSyncTimeUnit()));
        powerJobRequestParam.setTimeExpression(JSON.toJSONString(powerJobTimeExpressionParam));
        powerJobRequestParam.setProcessorInfo(TASK_PROCESSOR);
        powerJobRequestParam.setEnable(true);
        return powerJobRequestParam;
    }

    private String calcInterval(Integer syncTime, String syncTimeUnit) {
        TimeUnitEnum timeUnitEnum = TimeUnitEnum.getTimeUnitEnumByCode(syncTimeUnit);
        if (timeUnitEnum == null) {
            throw new RuntimeException("syncTimeUnit is error!");
        }
        switch (timeUnitEnum) {
            case hours -> {
                return String.valueOf(60 * 60 * syncTime);
            }
            case minutes -> {
                return String.valueOf(60 * syncTime);
            }
        }
        return null;
    }

    @Override
    public boolean deleteDataSource(List<String> dataSourceIdList) {
        try {
            Query query = new Query(CriteriaWrapper.where(DataSourceInfo::getId).in(dataSourceIdList));
            List<DataSourceInfo> existsInfos = datasourceInfoMapper.find(query);
            List<Long> jobIds = existsInfos.stream().map(DataSourceInfo::getJobId).filter(Objects::nonNull).toList();
            if (CollectionUtils.isNotEmpty(jobIds)) {
                // 删除之前的定时任务重新创建定时任务
                powerJobService.deleteJob(jobIds);
            }
            datasourceInfoMapper.remove(query);
        } catch (Exception e) {
            throw new RuntimeException("delete datasource error!", e);
        }
        return true;
    }

    @Override
    public Object getDataSourceList(SearchParam searchParam) {
        try {
            if (StringUtils.equals(searchParam.getOperation(), SearchParam.keyword) && StringUtils.isEmpty(searchParam.getKeywordValue())) {
                searchParam.setOperation(null);
            }
            Criteria criteria = genCriteria(searchParam);
            List<DataSourceInfo> resultList = CMDBTool.selectPageList(datasourceInfoMapper, criteria, searchParam);
            // 设置引擎类型名称及引擎名称
            List<Integer> engineIdList = resultList.stream().map(DataSourceInfo::getEngineId).toList();
            List<MwTPServerTable> mwTPServerTableList = mwTPServerService.getMwTPServerByServiceIds(engineIdList);
            Map<Integer, MwTPServerTable> engineId2ServerMap = mwTPServerTableList.stream().collect(Collectors.toMap(MwTPServerTable::getId, item -> item, (o1, o2) -> o1));
            resultList.forEach(item -> {
                item.setDatasourceTypeName(ServerTypeEnum.getDescByCode(item.getDatasourceType()));
                if (engineId2ServerMap.containsKey(item.getEngineId())) {
                    item.setEngineName(engineId2ServerMap.get(item.getEngineId()).getMonitoringServerName());
                }
            });
            ViewConvertContext context = new ViewConvertContext();
            return viewManager.getListViewObject(resultList, DataSourceInfo.class, searchParam, context);
        } catch (Exception e) {
            throw new RuntimeException("getDataSourceList error!", e);
        }
    }

    @Override
    public List<DataSourceSyncResultVo> syncByDataSourceIds(List<String> dataSourceIds) throws Exception {
        Query query = new Query(CriteriaWrapper.where(DataSourceInfo::getId).in(dataSourceIds));
        List<DataSourceInfo> dataSourceInfoList = datasourceInfoMapper.find(query);
        List<DataSourceMappingRuleInfo> params = JSONArray.parseArray(JSON.toJSONString(dataSourceInfoList), DataSourceMappingRuleInfo.class);
        // dubbo服务传递List<List<Long>>序列化不兼容 使用字符串传递
        params.forEach(item -> {
            item.setOrgsStr(JSON.toJSONString(item.getOrgs()));
            item.setOrgs(null);
        });
        return mwDatasourceSyncService.batchSyncData(params);
    }

    @Override
    public Object getSyncResultByDataSourceId(DataSourceSyncResultQueryParam dataSourceSyncResultQueryParam) throws Exception {
        if (StringUtils.isEmpty(dataSourceSyncResultQueryParam.getSortName())) {
            // 默认按照上报事件倒序排列
            dataSourceSyncResultQueryParam.setSortName(DatabaseUtils.getFieldName(DataSourceSyncResultInfo::getSyncTime));
            dataSourceSyncResultQueryParam.setSortOrder("DESC");
        }
        Criteria criteria = CriteriaWrapper.where(DataSourceSyncResultInfo::getDataSourceId).in(dataSourceSyncResultQueryParam.getDataSourceId());
        List<DataSourceSyncResultInfo> resultList = CMDBTool.selectPageList(datasourceSyncResultInfoMapper, criteria, dataSourceSyncResultQueryParam);
        ViewConvertContext context = new ViewConvertContext();
        return viewManager.getListViewObject(resultList, DataSourceSyncResultInfo.class, dataSourceSyncResultQueryParam, context);
    }

    @Override
    public Object getSyncInstanceByTaskId(DataSourceSyncResultQueryParam dataSourceSyncResultQueryParam) throws Exception {
        Criteria criteria = CriteriaWrapper.where(DataSourceSyncInstanceInfo::getTaskId).in(dataSourceSyncResultQueryParam.getTaskIdList());
        if (dataSourceSyncResultQueryParam.getAsyncResult()!=null){
            criteria.andOperator(CriteriaWrapper.where(DataSourceSyncInstanceInfo::isAsyncResult).is(dataSourceSyncResultQueryParam.getAsyncResult()));
        }
        List<DataSourceSyncInstanceInfo> resultList = CMDBTool.selectPageList(datasourceSyncInstanceInfoMapper, criteria, dataSourceSyncResultQueryParam);
        ViewConvertContext context = new ViewConvertContext();
        return viewManager.getListViewObject(resultList, DataSourceSyncInstanceInfo.class, dataSourceSyncResultQueryParam, context);
    }

    @Override
    public List<TableHeaderItem> getDataSourceTableHeader(String datasourceId) throws Exception {
        List<TableHeaderItem> resultList = new ArrayList<>();
        Query query = new Query(CriteriaWrapper.where(DataSourceInfo::getId).is(datasourceId));
        DataSourceInfo dataSourceInfo = datasourceInfoMapper.findOne(query);
        List<DataSourcePropertyRelInfo> mappingRuleList = dataSourceInfo.getMappingRule();
        TableHeaderItem tableHeaderItem;
        for (DataSourcePropertyRelInfo dataSourcePropertyRelInfo : mappingRuleList) {
            tableHeaderItem = new TableHeaderItem();
            tableHeaderItem.setField(dataSourcePropertyRelInfo.getTargetField().getValue());
            tableHeaderItem.setPropertyTypeId(dataSourcePropertyRelInfo.getTargetField().getPropertyType());
            tableHeaderItem.setHeaderName(dataSourcePropertyRelInfo.getTargetField().getLabel());
            resultList.add(tableHeaderItem);
        }
        return resultList;
    }
}
